#ifndef __UTILS_H__
#define __UTILS_H__

#include "FreeRTOS.h"
#include "task.h"
#include "cmsis_os2.h"

#ifdef __cplusplus
extern "C" {
#endif
#include "stdint.h"

extern osThreadId_t cpuTaskHandle;
extern const osThreadAttr_t cpuTask_attributes;
void cpu_task(void *params);

/**
 * @brief 物理信号到CAN信号的转换
 *        mode 转换模式 1：有偏移量有精度  2：只有偏移量  3：只有精度
 *        x    物理信号值
 *        y    偏移量
 *        z    精度
 */
#define PHY_TO_SIGNAL(mode, x, y, z)                 \
    ({                                               \
        typeof(x) _x = x;                            \
        typeof(y) _y = y;                            \
        typeof(z) _z = z;                            \
        uint32_t data = 0;                           \
        if (mode == 1)                               \
            data = (uint32_t)(((_x) - (_y)) / (_z)); \
        else if (mode == 2)                          \
            data = ((_x) - (_y));                    \
        else if (mode == 3)                          \
            data = (uint32_t)((_x) / (_z));          \
        data;                                        \
    })

/**
 * @brief CAN信号转换为实际的物理信号
 *        mode 转换模式 1：有偏移量有精度  2：只有偏移量  3：只有精度
 *        x    信号信号值
 *        y    偏移量
 *        z    精度
 */
#define SIGNAL_TO_PHY(mode, x, y, z)                 \
    ({                                               \
        typeof(x) _x = x;                            \
        typeof(y) _y = y;                            \
        typeof(z) _z = z;                            \
        uint32_t data = 0;                           \
        if (mode == 1)                               \
            data = (uint32_t)((_x) * (_z) + (_y));   \
        else if (mode == 2)                          \
            data = ((_x) + (_y));                    \
        else if (mode == 3)                          \
            data = (uint32_t)((_x) * (_z));          \
        data;                                        \
    })

/**
 * @brief 内联函数 float转long
 * @param  hex              输入的单精度浮点原值
 * @return long             输出对应的hex值
 */
static inline long f_to_h(float hex)
{
    return *(long *)&hex;
}

/**
 * @brief 内联函数 long转hex
 * @param  hex              输入的hex原值
 * @return float            输出对应的单精度浮点值
 */
static inline float h_to_f(long hex)
{
    return *(float *)&hex;
}

typedef struct
{
    uint32_t end_tick;
    uint32_t last_timeout;
    uint32_t event;
} timer_t;

typedef union
{
    uint16_t data16;
    uint8_t data8[2];
} undata16_t;

typedef union
{
    uint32_t data32;
    uint16_t data16[2];
    uint8_t data8[4];
} undata32_t;

typedef enum {
    PERIOD_TASK_FSM_START = 0,
    PERIOD_TASK_FSM_TIMEOUT
} PeriodTaskFSMState;

#define RUN_PERIODIC_TASK(CODE, period)                                     \
	{                                                                       \
		static PeriodTaskFSMState state = PERIOD_TASK_FSM_START;            \
		static uint32_t start_tick = 0;                                     \
		static uint32_t period_tick = 0;                                    \
		/* period change, fsm reset */                                      \
		if (period_tick != pdMS_TO_TICKS(period))                           \
		{                                                                   \
			period_tick = pdMS_TO_TICKS(period);                            \
			state = PERIOD_TASK_FSM_START;                                  \
		}                                                                   \
                                                                            \
		switch (state)                                                      \
		{                                                                   \
		case PERIOD_TASK_FSM_START:                                         \
			start_tick = xTaskGetTickCount();                               \
			state = PERIOD_TASK_FSM_TIMEOUT;                                \
			do                                                              \
			{                                                               \
				CODE;                                                       \
			} while (0);                                                    \
		case PERIOD_TASK_FSM_TIMEOUT:                                       \
			if (xTaskGetTickCount() - start_tick >= period_tick)            \
			{                                                               \
				state = PERIOD_TASK_FSM_START;                              \
			}                                                               \
			break;                                                          \
		default:                                                            \
			break;                                                          \
		}                                                                   \
	}


int16_t
usMBCRC16(char *pucFrame, int16_t usLen);
void clear_timer(timer_t *timer);
void set_timer(timer_t *timer, uint32_t event, uint32_t timeout);

double energy_calculate(double vol, double curr, uint32_t dt);

void event_set_bit(uint32_t bit);
uint32_t event_get_bit(uint32_t bit);
void event_clear_bit(uint32_t bit);

#ifdef __cplusplus
}
#endif

#endif  /* __UTILS_H__ */


